﻿
using System;
using System.Text;
using FacebookXmpp.Parse;

namespace FacebookXmpp.Stanzas
{
	internal class Challenge : Stanza
	{
		private string _response = string.Empty;

        public override string MessageToService()
        {
			if (_response != string.Empty)
			{
				Stream.RemoveHandler( this );
				Stream.AddHandler( new SuccessFailure( ) );            
			}

			return _response;
        }
		
		internal override StanzaListenCriteria ListenFor
		{
			get { return new StanzaListenCriteria( "challenge", StanzaListenCriteria.DefaultResponseTimeMilliseconds * 2); }
		}

        public override void ProcessResponse(MessageData recieved)
		{
            Stream.Dashboard.WorkerState("Authenticating");

			// Pull out the fields "Version" "Method" and "Nonce"
			var versionMethodNonce = new VersionMethodNonce( DecodeFrom64( recieved.Value.ToString() ) );

			// Create a response for Facebook with ApiKey, users SessionId etc.
			string composedResponse = CreateXFacebookPlatformResponse( versionMethodNonce, Stream.Dashboard );

			// Send NextResponse back to service
			_response = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + Convert.ToBase64String( Encoding.UTF8.GetBytes( composedResponse ) ) + "</response>";
		}

		private string CreateXFacebookPlatformResponse(VersionMethodNonce values, Dashboard dashboard)
		{
			string method = values.Method;
			string nonce = values.Nonce;
			
			TimeSpan t = (DateTime.UtcNow - new DateTime( 1970, 1, 1 ));
			long callId = (long)t.TotalSeconds;

            return "method=" + method
                + "&api_key=" + dashboard.FbAppKey
                + "&access_token=" + dashboard.FbAccessToken
                + "&call_id=" + callId
                + "&v=" + values.Version
                + "&nonce=" + nonce;
        }

		private string DecodeFrom64( string encodedData )
		{
			byte[] encodedDataAsBytes = Convert.FromBase64String( encodedData );
            string returnValue = Encoding.UTF8.GetString(encodedDataAsBytes, 0, encodedDataAsBytes.Length);
			return returnValue;
		}

		private class VersionMethodNonce
		{
			public string Version;
			public string Method;
			public string Nonce;

			public VersionMethodNonce( string decode )
			{
				string[] pairs = decode.Split( '&' );
				if (pairs.Length != 3)
					throw new ArgumentException( "decode '{0}' is not in expected format", decode );

				foreach (string keyEqualsValue in pairs)
				{
					string[] keyValue = keyEqualsValue.Split( '=' );
					if (keyValue.Length != 2)
						throw new ArgumentException( "decode '{0}' is not in expected format", decode );

					switch (keyValue[0].ToLower())
					{
						case "version":
							Version = keyValue[1];
							break;
						case "method":
							Method = keyValue[1];
							break;
						case "nonce":
							Nonce = keyValue[1];
							break;
					}
				}
			}
		}
	}
}
